<style lang="scss" src="./index.scss"></style>
<template>
    <div class="ask-city-cascader-box">
        <el-popover
            placement="bottom-start"
            width="500"
            popper-class="ask-city-cascader-box"
            ref="popoverRef"
            @show="onPopperShow"
            @hide="onPopperHide"
            :trigger="trigger"
        >
            <div class="city-cascader-panel__header">
                <nav class="city-cascader-panel__header-nav">
                    <ul class="city-cascader-panel__header-list">
                        <template v-for="item in renderTabs">
                            <transition
                                name="page-transition-slide-in-top"
                                :key="item.id"
                            >
                                <li
                                    tag="li"
                                    class="city-cascader-panel__header-item"
                                    :class="{
                                        active: currentActive === item.value
                                    }"
                                    :key="item.id"
                                    v-show="item.show"
                                    @click="handleTabClick(item)"
                                >
                                    <div
                                        class="city-cascader-panel__header-text"
                                    >
                                        {{
                                            currentValue[item.value].name ||
                                                item.label
                                        }}
                                    </div>
                                </li>
                            </transition>
                        </template>
                    </ul>
                </nav>
            </div>
            <div
                class="city-cascader-panel__body"
                :class="{ recommend: recommend }"
                ref="popoverBodyRef"
                v-auto-focus
            >
                <ask-city-recommend-panel
                    v-if="recommend"
                    @item-active="handleItemActive"
                    v-model="activeCode"
                >
                </ask-city-recommend-panel>
                <div class="city-cascader-panel__body-box">
                    <template v-for="item in renderTabs">
                        <component
                            v-bind:is="'ask-city-panel-' + item.value"
                            v-model="currentValue[item.value].code"
                            :key="item.id"
                            :beforeClick="beforeItemClick"
                            :show="currentActive === item.value"
                            :showNot="
                                showNot | filterShowNot(item.value, renderTabs)
                            "
                            @item-active="handleItemActive"
                            :disabled="disabled"
                            :ref="'askCity' + item.value + 'PanelRef'"
                        ></component>
                    </template>
                </div>
            </div>
            <!-- <div class="city-cascader-panel__footer" v-if="anywhere">
                <button type="button" class="sure-btn" @click="handleAnywhereClick">确定</button>
            </div> -->
            <div slot="reference" class="city-cascader__label">
                <div
                    class="city-cascader__label-text"
                    :class="{ placeholder: !currentLabel }"
                >
                    {{ currentLabel || placeholder || "请选择" }}
                </div>
                <div class="city-cascader__label-icon">
                    <div
                        class="city-cascader__label-icon-box"
                        :class="{
                            active: popperShow,
                            'has-label': currentLabel
                        }"
                    >
                        <i class="el-input__icon el-icon-arrow-up"></i>
                        <i
                            class="close-icon el-input__icon el-icon-circle-close"
                            v-if="activeCode !== '' && clear && !disabled"
                            @click.stop="clearValue"
                        ></i>
                    </div>
                </div>
            </div>
        </el-popover>
        <!-- {{currentActive}}-{{currentValue}} -->
    </div>
</template>
<script lang="ts">
import {
    Vue,
    Component,
    Provide,
    Prop,
    Watch
} from "vue-property-decorator";

import AskCityPanelProvince from "./panel/province.vue";
import AskCityPanelCity from "./panel/city.vue";
import AskCityPanelArea from "./panel/area.vue";
import AskCityPanelTown from "./panel/town.vue";
import AskCityPanelVillage from "./panel/village.vue";
import AskCityRecommendPanel from "./panel/recommend.vue";
import { merge } from "@/utils";

@Component({
    model: {
        prop: "value",
        event: "input"
    },
    name: "CityCascader",
    components: {
        AskCityPanelProvince,
        AskCityPanelCity,
        AskCityPanelArea,
        AskCityPanelTown,
        AskCityPanelVillage,
        AskCityRecommendPanel
    },
    filters: {
        filterShowNot(value: any, key: any, renderTabs: any) {
            if (typeof value === "boolean") {
                return value && key === renderTabs[renderTabs.length - 1].value;
            }
            const _sourceIndex = renderTabs.findIndex((cur: any) => cur.value === key);
            const _valueIndex = renderTabs.findIndex(
                (cur: any) => cur.value === value
            );
            return _valueIndex <= _sourceIndex;
        }
    }
})
export default class CityCascader extends Vue {
    @Provide() main = this;
    @Prop({
        type: [String, Number],
        default: "" // value 选中值 同activeCode 是一个东西
    })
    readonly value!: string | number;

    @Prop({
        type: String,
        default: "请选择省市区"
    })
    readonly placeholder!: string;

    @Prop({
        type: String,
        default: "click" // 触发方式 hover|click
    })
    readonly trigger!: string;

    @Prop({
        type: Boolean,
        default: false // 是否禁用
    })
    readonly disabled!: boolean;

    @Prop({
        type: String,
        default: "fade-in-linear" // 过渡动画
    })
    readonly transition!: string;

    @Prop({
        type: String,
        default: "/" // label展示的分割符
    })
    readonly step!: string;

    @Prop({
        type: Object,
        default: () => {
            return { boundariesElement: "body", gpuAcceleration: false }; // popper配置
        }
    })
    readonly popperOptions!: any;

    @Prop({
        type: [Boolean, String], // 当且仅当anywhere关闭时生效
        // 开启之后在 showModel 配置项大于一个的时候将会在最后一级增加选择暂不选择的选项
        // 为String的时候则在指定位置以及之后增加暂不选择
        default: false,
        validator: val => {
            const keyList = ["city", "area", "town", "village"];
            if (typeof val === "string") {
                return keyList.includes(val);
            } else {
                return true;
            }
        }
    })
    readonly showNot!: string | boolean;

    @Prop({
        type: Array,
        default: () => {
            // 控制展示的tab和内容
            // 展示省 ["province"]
            // 展示省，市 ["province","city"]
            // 展示省，市，区 ["province", "city", "area"]
            // 展示省，市，区，镇 ["province", "city", "area", "town"]
            // 展示省，市，区，镇，村 ["province", "city", "area", "town", "village"]
            return ["province", "city", "area", "town"];
        }
    })
    readonly showModel!: any[];

    @Prop({
        type: Boolean,
        default: false // 清空选中值
    })
    readonly clear!: boolean;

    @Prop({
        type: Boolean,
        default: false // 显示推荐列表
    })
    readonly recommend!: boolean;

    @Prop({
        type: Function,
        default: () => true
    })
    readonly beforeItemClick!: any;

    // notAuth 代表不需要根据用户权限拉取
    // auth代表需要
    // authArea代表需要，并且省市区只会获取已开通的列表
    @Prop({
        type: String,
        default: "notAuth"
    })
    readonly cityQuery!: string;

    @Watch("value", { immediate: true })
    onValueChange(val: string, oldVal: string) {
        // console.log(this.activeCode, this.value);
        if (this.activeCode !== this.value) {
            this.activeCode = "" + this.value;
            this.$nextTick(() => {
                this.init();
            });
        }
        if (oldVal && val === "") {
            this.resetState();
        }
    }

    @Watch("cityQuery", { immediate: true })
    onCityQueryChange(val: string, oldVal: string) {
        if (oldVal === "auth" && val === "notAuth") {
            this.resetState();
        }
    }

    // 获取popper展示关闭的状态值
    popperShow = false;
    // 根据currentValue构建的label的展示值
    currentLabel = "";
    // 当前选中的省市区镇的相关信息
    currentValue = {
        province: {
            prev: "",
            code: "",
            name: ""
        },
        city: {
            prev: "",
            code: "",
            name: ""
        },
        area: {
            prev: "",
            code: "",
            name: ""
        },
        town: {
            prev: "",
            code: "",
            name: ""
        },
        village: {
            prev: "",
            code: "",
            name: ""
        }
    };

    // popper打开时候或者构建label的时候的currentValue值，用来做展示优化的
    currentOldValue = {};
    // 当前的currentValue中最后一级的code值 判断最后一级的标准是showModel
    activeCode = "";
    // 当前tabbar展示的选中值
    currentActive = "";

    get renderTabs() {
        const _res = {
            province: {
                label: "省",
                id: 1
            },
            city: {
                label: "市",
                id: 2
            },
            area: {
                label: "区",
                id: 3
            },
            town: {
                label: "城镇",
                id: 4
            },
            village: {
                label: "村",
                id: 5
            }
        };
        const res: any[] = [];
        let setState = false;
        Object.keys(_res).forEach((key: any) => {
            if (this.showModel.includes(key)) {
                const cur: any = {
                    ..._res[key],
                    show: false,
                    value: key
                };
                if (this.currentActive) {
                    cur.show = !setState;
                    if (this.currentActive === key) {
                        setState = true;
                    }
                }
                res.push(cur);
            }
        });
        return res;
    }

    clearValue() {
        this.$emit("input", "");
    }

    /**
     * 监听popper展示事件
     */
    onPopperShow() {
        this.popperShow = true;
        if (this.activeCode === "") {
            this.init();
        }
        let prevKey = "province";
        let _index = 0;
        for (let i = 0; i < this.renderTabs.length; i++) {
            const cur: any = this.currentValue[this.renderTabs[i].value];
            this.renderTabs[i].show = true;
            if (cur.code === "" || cur.name === "") {
                break;
            }
            _index = i;
            prevKey = this.renderTabs[i].value;
            const isForceEmpty = ["暂不选择", "全部"].includes(cur.name);
            if (isForceEmpty) break;
        }
        this.currentActive = prevKey;
        this.handleChildNode(this.renderTabs[_index]);
    }

    /**
     * 监听popper隐藏事件
     */
    onPopperHide() {
        this.popperShow = false;
        // 隐藏的时候判断当前是否有未选中的节点，如果存在则恢复成currentOldValue的值
        for (let i = 0; i < this.renderTabs.length; i++) {
            const cur: any = this.currentValue[this.renderTabs[i].value];
            if (cur.name === "全部") {
                break;
            }
            if (cur.code === "" || cur.name === "") {
                this.currentValue = merge(true, {}, this.currentOldValue);
                this.init();
                break;
            }
        }
    }

    /**
     * 根据currentValue和showModel来构建选中的label展示值
     */
    buildLabel() {
        let _label = "";
        this.renderTabs.forEach((cur: any) => {
            const currentStep: any = this.currentValue[cur.value];
            if (
                currentStep.code &&
                currentStep.name &&
                currentStep.name !== "暂不选择"
            ) {
                _label += this.step + currentStep.name;
            }
        });
        this.currentLabel = _label.substring(1);
        this.currentOldValue = merge(true, {}, this.currentValue);
    }

    /**
     * 点击顶部tabbar的时候跟新节点展示位置和对应位置的列表
     */
    handleTabClick(item: any) {
        this.currentActive = item.value;
        this.handleChildNode(item);
    }

    /**
     * 触发对应子节点的init方法初始化
     */
    async handleChildNode(item: any) {
        const curInfo = this.getStepInfo(item);
        const response = await (this.$refs["askCity" + item.value + "PanelRef"] as any)[0].initData(curInfo.prev);
        return response;
    }

    /**
     * 获取对应渲染区域的节点值，如果不符合条件则返回null，这里还会根据showModel做二次判断
     */
    getStepInfo(item: any) {
        const _info = this.currentValue[item.value];
        return _info;
    }

    /**
     * 设置currentValue
     */
    setCurrentValue(key: any, item: any) {
        this.currentValue[key].prev = item.prev;
        this.currentValue[key].code = item.code;
        this.currentValue[key].name = item.name;
    }

    handleItemActive(key: any, item: any) {
        this.setCurrentValue(key, {
            ...item,
            prev: this.currentValue[key].prev
        });
        const modelIndex = this.renderTabs.findIndex((cur: any) => cur.value === key);
        // console.log(modelIndex, key, item);
        if (modelIndex === -1 || modelIndex === this.renderTabs.length - 1) {
            this.lastStep(key);
            return;
        }
        const isForceEmpty = ["暂不选择", "全部"].includes(item.name);
        for (let i = modelIndex + 1; i < this.renderTabs.length; i++) {
            const cur: any = this.renderTabs[i];
            cur.show = isForceEmpty ? false : i === modelIndex + 1;
            this.setCurrentValue(cur.value, {
                code: "",
                name: "",
                prev: isForceEmpty ? "" : i === modelIndex + 1 ? item.code : ""
            });
            (this.$refs["askCity" + cur.value + "PanelRef"] as any)[0].resetState();
        }
        if (isForceEmpty) {
            this.lastStep(key);
            return;
        }
        // console.log(this.renderTabs[modelIndex + 1]);
        this.handleTabClick(this.renderTabs[modelIndex + 1]);
    }

    /**
     * 构建lable 数据双向绑定 触发change事件 关闭popper
     */
    lastStep(lastKey: any, closePopover = true) {
        this.buildLabel();
        this.activeCode = this.currentValue[lastKey].code;
        this.$emit("input", this.activeCode);
        const currentValue = {};
        Object.keys(this.currentValue).forEach((key: any) => {
            const cur = this.currentValue[key];
            if (cur.name === "全部" || cur.name === "暂不选择") {
                currentValue[key] = {
                    ...cur,
                    name: ""
                };
            } else {
                currentValue[key] = cur;
            }
        });
        this.$emit("change", currentValue);
        const popoverRef: any = this.$refs.popoverRef;
        closePopover && popoverRef.handleDocumentClick(document);
    }

    /**
     * 当clear开启时点击清空按钮会删除所有节点和重置初始值并关闭popper
     */
    resetState() {
        this.renderTabs.forEach((item, $i) => {
            this.renderTabs[$i].show = $i === 0;
            this.setCurrentValue(item.value, {
                code: "",
                name: "",
                prev: ""
            });
            const refs: any = this.$refs["askCity" + item.value + "PanelRef"];
            if (refs && refs[0]) {
                refs[0].resetState();
            }
        });
        this.lastStep(this.renderTabs[0].value);
    }

    /**
     * 循环判断配置列表的最后一个节点
     */
    async init() {
        this.initCityInfo();
    }

    /**
     * 初始化citycode对应的列表值
     */
    async initCityInfo() {
        let _activeCode = this.activeCode;
        const code = {
            province: "",
            provinceOld: "",
            city: "",
            cityOld: "",
            area: "",
            areaOld: "",
            town: "",
            townOld: "",
            village: "",
            villageOld: ""
        };
        if (_activeCode && _activeCode !== "-1") {
            if (_activeCode.length < 2) {
                _activeCode = _activeCode + "0000";
            }
            code.provinceOld = "";
            code.province = _activeCode.substring(0, 2) + "0000";
            code.cityOld = code.province;
            code.city = _activeCode.substring(0, 4) + "00";
            code.areaOld = code.city;
            code.area = _activeCode.substring(0, 6);
            code.townOld = code.area;
            code.town = _activeCode.substring(0, 12);
            code.villageOld = code.town;
            code.village = _activeCode;
        }
        if (_activeCode === "-1") {
            code.province = _activeCode;
        }
        const response = await this.initChildData(0, code);
        this.lastStep(response.item.value, false);
    }

    /**
     * 设置每个panel的选中值和初始化panel的列表
     */
    async initChildData(index: any, code: any) {
        const item = this.renderTabs[index];
        const value = code[item.value];
        const cityCode = code[item.value + "Old"];
        if (!value && item.value !== "province") {
            return;
        }
        this.setCurrentValue(item.value, {
            prev: cityCode,
            code: value,
            name: ""
        });
        await this.$nextTick();
        item.show = true;
        const response = await (this.$refs["askCity" + item.value + "PanelRef"] as any)[0].initData(cityCode);
        this.currentValue[item.value].name = (response && response.name) || "";
        index++;
        const isForceEmpty = ["暂不选择", "全部"].includes(
            response && response.name
        );
        // console.log(isForceEmpty, response, index);
        if (
            response &&
            response.name &&
            index < this.renderTabs.length &&
            !isForceEmpty
        ) {
            const res: any = await this.initChildData(index, code);
            return res;
        }
        return {
            cityPanel: response,
            item: item
        };
    }
}
</script>
